#include "StdAfx.h"

#include <time.h>
#include <math.h>
#include <SFML/System/Randomizer.hpp>

#include "CarrierStream.h"
#include "Locator.h"
#include "SoundBank.h"
#include "WaveSample.h"
#include "Settings.h"

/*
static void func1(WaveSample* waveSample)
{
	//y''+sin y=cos x
	double* dAr = new double[waveSample->length];
	dAr[0] = 0;
	dAr[1] = 1;
	for(int i=1; i<waveSample->length; i++)
	{
		double d = cos((double)i-2)-sin(dAr[i-2]);
		dAr[i] = d;
	}
	for(int i=0; i<waveSample->length; i++)
	{
		int y = dAr[i]*SHORT_MAX;
		waveSample->data[i] = y;
	}
	delete dAr;
}
*/
/*
static void func1(WaveSample* waveSample)
{
	//y = sin(x)+sin(sqrt(2)*x)
	for(int i=0; i<waveSample->length; i++)
	{
		int y = sin((double)i)+sin(sqrt(2.)*i) * SHORT_MAX;
		waveSample->data[i] = y;
	}
}
*/
static sf::Randomizer randomizer;

static void func1(WaveSample* waveSample)
{
	//semi random inharmonic frequency
	int freq = 48000 / waveSample->length;
	SoundBank* bank = Locator::Instance()->GetSoundBank();
	WaveSample* bankSample = bank->GetWaveSample(WaveForm::Triangular, freq*3.7, 48000);
	for(int i=0; i<waveSample->length; i++)
	{
		waveSample->data[i] = bankSample->data[i];
	}
}

static void func2(WaveSample* waveSample)
{
	//random
	for(int i=0; i<waveSample->length; i++)
	{
		waveSample->data[i] = randomizer.Random(SHORT_MIN, SHORT_MAX);
	}
}
static void func3(WaveSample* waveSample)
{
	//random
	for(int i=0; i<waveSample->length; i++)
	{
		waveSample->data[i] = randomizer.Random(SHORT_MIN, SHORT_MAX);
	}
}

CarrierStream::CarrierStream(void)
{
	chunkSize = 0;
	sampleRate = 0;

	squareComponent = new WaveComponent();
	squareComponent->waveForm = Square;

	triComponent = new WaveComponent();
	triComponent->waveForm = Triangular;

	sineComponent = new WaveComponent();
	sineComponent->waveForm = Sine;
	sineF2Component = new WaveComponent();
	sineF2Component->waveForm = Triangular;
	sineF3Component = new WaveComponent();
	sineF3Component->waveForm = Triangular;
	sineF4Component = new WaveComponent();
	sineF4Component->waveForm = Triangular;
	sineF5Component = new WaveComponent();
	sineF5Component->waveForm = Triangular;
	sineF6Component = new WaveComponent();
	sineF6Component->waveForm = Triangular;
	sineF7Component = new WaveComponent();
	sineF7Component->waveForm = Triangular;

	sawtoothComponent = new WaveComponent();
	sawtoothComponent->waveForm = Sawtooth;

	randomComponent = new WaveComponent();
	randomComponent->waveForm = Random;

	func1Component = new WaveComponent();
	func1Component->waveForm = UserFunc1;
	func2Component = new WaveComponent();
	func2Component->waveForm = UserFunc2;
	func3Component = new WaveComponent();
	func3Component->waveForm = UserFunc3;

	userFunc1 = func1;
	userFunc2 = func2;
	userFunc3 = func3;

}


CarrierStream::~CarrierStream(void)
{
	delete squareComponent;
	delete triComponent;
	delete sineComponent;
	delete sineF2Component;
	delete sineF3Component;
	delete sineF4Component;
	delete sineF5Component;
	delete sineF6Component;
	delete sineF7Component;
	delete sawtoothComponent;
	delete randomComponent;
	delete func1Component;
	delete func2Component;
	delete func3Component;
}

void CarrierStream::Initialize(int sampleRate, int chunkSize)
{
	this->sampleRate = sampleRate;
	this->chunkSize = chunkSize;
}


/*
void addComponent(Note* note, WaveComponent* component, double freq, double freqVar, int intensity, int intensityVariation)
{
	component->frequencyStart = freq + randomizer.Random((float)-freqVar, (float)freqVar);
	component->intensity = intensity;
	component->intensityVariation = intensityVariation;
	note->AddComponent(component);
}
*/

void CarrierStream::DeleteComponentSample(WaveComponent* component)
{
	Sample* s = component->sample;
	if(s) {
		component->sample = NULL;
		//delete s;
	}
}

void CarrierStream::DeleteComponentSamples()
{
	DeleteComponentSample(triComponent);
	DeleteComponentSample(squareComponent);
	DeleteComponentSample(sawtoothComponent);
	DeleteComponentSample(randomComponent);
	DeleteComponentSample(func1Component);
	DeleteComponentSample(func2Component);
	DeleteComponentSample(func3Component);
	DeleteComponentSample(sineComponent);
	DeleteComponentSample(sineF2Component);
	DeleteComponentSample(sineF3Component);
	DeleteComponentSample(sineF4Component);
	DeleteComponentSample(sineF5Component);
	DeleteComponentSample(sineF6Component);
	DeleteComponentSample(sineF7Component);
}


void CarrierStream::UpdateComponent(WaveComponent* component, int frequency, int rate, double intensity)
{
	int freqVar = Locator::Instance()->GetSettings()->GetCarrier()->frequencyVariation;
	if(!freqVar && (int)component->frequencyStart==frequency && component->intensity==intensity)
	{
		return;
	}
	else
	{
		component->frequencyStart = frequency;
		component->intensity = intensity;
		component->sample = NULL;
	}

	if(intensity>0.03)
	{
		SoundBank* bank = Locator::Instance()->GetSoundBank();
		int inc = 0;
		if(freqVar) inc = randomizer.Random(-freqVar, freqVar);
		if(component->waveForm==UserFunc1 || component->waveForm==UserFunc2 || component->waveForm==UserFunc3)
		{
			void(*WaveSampleCb)(WaveSample*) = NULL;
			switch(component->waveForm)
			{
			case UserFunc1:
				WaveSampleCb = userFunc1;
				break;
			case UserFunc2:
				WaveSampleCb = userFunc2;
				break;
			case UserFunc3:
				WaveSampleCb = userFunc3;
				break;
			}
			if(component->waveForm==UserFunc1)
			{
				//float r = randomizer.Random((float)2.0,4.0);
				//float r = 2.5;
				//float r = sqrt(2.0);
				//float r = 2.718281828;
				float r = 3.14;
				component->sample = bank->GetWaveSample(Triangular, frequency*r, rate, intensity);
			}
			else
			{
				component->sample = bank->GetWaveSample(component->waveForm, WaveSampleCb, frequency+inc, rate, intensity);
			}
		}
		else
		{
			component->sample = bank->GetWaveSample(component->waveForm, frequency+inc, rate, intensity);
		}		
	}
}

void CarrierStream::AddComponent(Sample* sample, WaveComponent* component)
{
	if(!component->sample) return;

	int j=component->waveIndex;
	for(int i=0; i<sample->length; i++)
	{
		for(; j<component->sample->length && i<sample->length; j++, i++)
		{
			sample->data[i] += component->sample->data[j];
		}
		if(i<sample->length)
		{
			j = 0;
		}
	}
	component->waveIndex = j;
}

static int totalGetDataTime = 0;
static int countGetData = 0;
static int lastFreq = 0;
static int lastWn = 0;
static int lastTri = 0;
static int lastSq = 0;
static int lastSaw = 0;
static int lastSine = 0;
static int lastRandom = 0;
static int lastFunc1 = 0;

Sample* CarrierStream::GetData()
{
	return GetData(Locator::Instance()->GetSettings()->GetCarrier()->frequency, true);
}

Sample* CarrierStream::GetData(int freq, bool voiced)
{
	if(freq<10)
	{
		/*
		if(voiced)
		{
			Sample* sample = new Sample(chunkSize);
			sample->AddWhiteNoise(1.0);
			//sample->AddBrownNoise(1.0);
			return sample;
		}
		else
		*/
		{
			return new Sample(chunkSize);
		}

	}

	clock_t start, finish;
	start = clock();

	Settings::CarrierSettings* settings = Locator::Instance()->GetSettings()->GetCarrier();
	double freqVar = (double) settings->frequencyVariation;
	int bn = settings->brownNoise;
	int wn = settings->whiteNoise;
	int tri = settings->triangular;
	int sq = settings->square;
	int saw = settings->sawtooth;
	int sine = settings->sine;
	int sineF2 = settings->sineF2;
	int sineF3 = settings->sineF3;
	int sineF4 = settings->sineF4;
	int sineF5 = settings->sineF5;
	int sineF6 = settings->sineF6;
	int sineF7 = settings->sineF7;
	int rnd = settings->random;
	int func1 = settings->function1;
	int func2 = settings->function2;
	int func3 = settings->function3;
	int totalIntensity = bn+wn+tri+sq+saw+sine+sineF2+sineF3+sineF4+sineF5+sineF6+sineF7+rnd+func1+func2;
	double wnD = (double)wn / (double)totalIntensity;
	double bnD = (double)bn / (double)totalIntensity;
	//if(freq!=lastFreq || wn!=lastWn || tri!=lastTri || sq!=lastSq ||
	//	saw!=lastSaw || sine!=lastSine || rnd!=lastRandom || func1!=lastFunc1)
	//{
		//delete old samples and create new ones
		//DeleteComponentSamples();
		lastFreq = freq;
		lastWn = wn;
		lastTri = tri;
		lastSq = sq;
		lastSaw = saw;
		lastSine = sine;
		lastRandom = rnd;
		lastFunc1 = func1;

		double triD = (double)tri / (double)totalIntensity;
		double sqD = (double)sq / (double)totalIntensity;
		double sawD = (double)saw / (double)totalIntensity;
		double sineD = (double)sine / (double)totalIntensity;
		double sineF2D = (double)sineF2 / (double)totalIntensity;
		double sineF3D = (double)sineF3 / (double)totalIntensity;
		double sineF4D = (double)sineF4 / (double)totalIntensity;
		double sineF5D = (double)sineF5 / (double)totalIntensity;
		double sineF6D = (double)sineF6 / (double)totalIntensity;
		double sineF7D = (double)sineF7 / (double)totalIntensity;
		double rndD = (double)rnd / (double)totalIntensity;
		double func1D = (double)func1 / (double)totalIntensity;
		double func2D = (double)func2 / (double)totalIntensity;
		double func3D = (double)func3 / (double)totalIntensity;

		UpdateComponent(triComponent, freq, sampleRate, triD);
		UpdateComponent(squareComponent, freq, sampleRate, sqD);
		UpdateComponent(sawtoothComponent, freq, sampleRate, sawD);
		UpdateComponent(sineComponent, freq, sampleRate, sineD);
		UpdateComponent(sineF2Component, freq*3, sampleRate, sineF2D);
		UpdateComponent(sineF3Component, freq*6, sampleRate, sineF3D);
		UpdateComponent(sineF4Component, freq*23, sampleRate, sineF4D);
		UpdateComponent(sineF5Component, freq*5, sampleRate, sineF5D);
		UpdateComponent(sineF6Component, freq*6, sampleRate, sineF6D);
		UpdateComponent(sineF7Component, freq*7, sampleRate, sineF7D);
		UpdateComponent(randomComponent, freq, sampleRate, rndD);
		UpdateComponent(func1Component, freq, sampleRate, func1D);
		UpdateComponent(func2Component, freq, sampleRate, func2D);
		UpdateComponent(func3Component, freq, sampleRate, func3D);
	//}

	Sample* sample = new Sample(chunkSize);
	AddComponent(sample, triComponent);
	AddComponent(sample, squareComponent);
	AddComponent(sample, sawtoothComponent);
	AddComponent(sample, sineComponent);
	AddComponent(sample, sineF2Component);
	AddComponent(sample, sineF3Component);
	AddComponent(sample, sineF4Component);
	AddComponent(sample, sineF5Component);
	AddComponent(sample, sineF6Component);
	AddComponent(sample, sineF7Component);
	AddComponent(sample, randomComponent);
	AddComponent(sample, func1Component);
	AddComponent(sample, func2Component);
	AddComponent(sample, func3Component);
	sample->AddWhiteNoise(wnD);
	sample->AddBrownNoise(bnD);
	/*
	for(int i=0; i<sample->length; i++)
	{
		short add = (double)randomizer.Random(SHORT_MIN, SHORT_MAX) * wnD;
		sample->data[i] += add;
	}
	*/

	finish = clock();
	int time = finish-start;
	countGetData++;
	totalGetDataTime += time;

	return sample;
}
